package com.allen.study.test_common.utils;

import com.allen.study.test_common.exception.CamundaProcessException;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricActivityInstanceQuery;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.DeploymentBuilder;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
import org.camunda.bpm.model.bpmn.instance.UserTask;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName: CamundaUtils
 * @Author: AllenSun
 * @Date: 2025/2/20 下午11:42
 */
// Camunda 工具类，封装了所有用到的 Camunda 方法
// http://localhost:8081/camunda/
@Slf4j
public class CamundaUtils {
    private static final ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    private static final RuntimeService runtimeService = processEngine.getRuntimeService();
    private static final TaskService taskService = processEngine.getTaskService();
    private static final RepositoryService repositoryService = processEngine.getRepositoryService();
    private static final HistoryService historyService = processEngine.getHistoryService();

    /**
     * 通过类路径部署流程
     * @param deploymentName 部署名称
     * @param classpathResource 类路径下的资源名称
     * @return 部署对象
     */
    public static Deployment deployProcessByClasspath(String deploymentName, String classpathResource) {
        Deployment deploy = null;
        try {
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                    .name(deploymentName)
                    .addClasspathResource(classpathResource);
            deploy = deploymentBuilder.deploy();
        } catch (Exception e) {
            log.error("流程部署失败:{}",e.getMessage());
            throw new CamundaProcessException("流程部署失败");
        }
        log.info("流程部署成功:{}",deploy.getId());

        return deploy;
    }


    /**
     * 根据部署key查询部署的流程
     * @MethodName: listProcessInstanceByKey
     * @Author: AllenSun
     * @Date: 2025/2/22 下午11:56
     */
    public static ProcessDefinition selectProcessDefinitionByKey(String processDefinitionKey) {
        // 根据流程定义键和业务键查询流程实例
        ProcessDefinition processDefinition = null;
        try {
            processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(processDefinitionKey)
                    .latestVersion()
                    .singleResult();
        } catch (Exception e) {
            log.error("查询部署流程信息失败：{}",e.getMessage());
            throw new CamundaProcessException("查询失败");
        }
        return processDefinition;
    }

    /**
     * 判断流程是否已部署
     * @MethodName: isProcessDefinitionExists
     * @Author: AllenSun
     * @Date: 2025/2/22 下午11:59
     */
    public static boolean isProcessDefinitionExists(String processDefinitionKey) {
        return selectProcessDefinitionByKey(processDefinitionKey) != null;
    }


    /**
     * 启动流程实例
     * @param processDefinitionKey 流程定义的 Key，对应 BPMN 文件中的流程定义 ID
     * @param variables 流程变量，用于传递业务数据
     * @return 流程实例 ID
     */
    public static String startProcessInstance(String processDefinitionKey, Map<String, Object> variables) {
        if(!isProcessDefinitionExists(processDefinitionKey)){
            throw new CamundaProcessException("流程未部署");
        }
        ProcessInstance processInstance = null;
        try {
            processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
        } catch (Exception e) {
            log.error("流程启动失败:{}:{}",processDefinitionKey,e.getMessage());
            throw new CamundaProcessException("流程启动失败");
        }
        log.info("流程启动成功：{}",processDefinitionKey);
        return processInstance.getId();
    }

    /**
     * 判断指定流程定义键和业务键的流程实例是否已经创建
     * @param processDefinitionKey 流程定义键
     * @return 如果流程实例已存在返回 true，否则返回 false
     */
    public static boolean isProcessInstanceExists(String processDefinitionKey) {
        // 根据流程定义键和业务键查询流程实例
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .list();
        return !processInstances.isEmpty();
    }

    /**
     * 完成任务
     * @param taskId 任务 ID
     * @param variables 任务变量，用于传递审批结果和理由等信息
     */
    public static void completeTask(String taskId, Map<String, Object> variables) {
        taskService.complete(taskId, variables);
    }

    /**
     * 获取指定用户的待办任务列表
     * @param assignee 任务负责人的工号
     * @param pageNum 页码
     * @param pageSize 每页数量
     * @return 待办任务列表
     */
    public static List<Task> getTasksByAssignee(String assignee, int pageNum, int pageSize) {
        List<Task> tasks = taskService.createTaskQuery()
                .taskAssignee(assignee)
                .listPage((pageNum - 1) * pageSize, pageSize);
        return tasks;
    }

    /**
     * 获取指定用户的已办任务列表
     * @param assignee 任务负责人的工号
     * @param pageNum 页码
     * @param pageSize 每页数量
     * @return 已办任务列表
     */
    public static List<HistoricTaskInstance> getCompletedTasksByAssignee(String assignee, int pageNum, int pageSize) {
        return historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(assignee)
                .finished()
                .listPage((pageNum - 1) * pageSize, pageSize);
    }

    /**
     * 获取指定流程实例的历史活动实例列表
     * @param processInstanceId 流程实例 ID
     * @return 历史活动实例列表
     */
    public static List<HistoricActivityInstance> getHistoricActivityInstances(String processInstanceId) {
        HistoricActivityInstanceQuery query = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId);
        return query.list();
    }

    /**
     * 获取指定流程实例的历史流程实例
     * @param processInstanceId 流程实例 ID
     * @return 历史流程实例
     */
    public static HistoricProcessInstance getHistoricProcessInstance(String processInstanceId) {
        return historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    /**
     * 终止流程实例
     * @param processInstanceId 流程实例 ID
     * @param deleteReason 终止原因
     */
    public static void terminateProcessInstance(String processInstanceId, String deleteReason) {
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
    }

    /**
     * 获取指定流程实例的当前任务
     * @param processInstanceId 流程实例 ID
     * @return 当前任务
     */
    public static Task getCurrentTask(String processInstanceId) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
    }

    /**
     * 转交任务
     * @MethodName: transferTask
     * @Author: AllenSun
     * @Date: 2025/2/23 下午3:37
     */
    public static Boolean transferTask(String taskId, String assigneeId){
        try {
            taskService.setAssignee(taskId, assigneeId);
        } catch (Exception e) {
            log.error("任务转交失败：{}",e.getMessage());
            throw new CamundaProcessException("任务转交失败");
        }
        return true;
    }


    /**
     * 驳回处理
     * @MethodName: rejectTask
     * @Author: AllenSun
     * @Date: 2025/2/23 下午9:06
     */
    public static Boolean rejectTask(String processInstanceId, String targetNodeId){
        try {
            //获取当前环节实例 这是个树结构
            ActivityInstance activity = runtimeService.getActivityInstance(processInstanceId);

            //获取实例的流程定义 这里主要是为了拿到节点前的那条线的Id
            List<HistoricActivityInstance> hisActivityList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished().list();
            List<String> nodeIds = hisActivityList.stream().map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());

            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            BpmnModelInstance bpmnModel = repositoryService.getBpmnModelInstance(processInstance.getProcessDefinitionId());
            ModelElementInstance modelElement = bpmnModel.getModelElementById(targetNodeId);
            UserTask userTask = (UserTask) modelElement;
            Collection<SequenceFlow> incoming = userTask.getIncoming();
            String transitionId = "";
            for (SequenceFlow sequenceFlow : incoming) {
                FlowNode source = sequenceFlow.getSource();
                if (nodeIds.contains(source.getId())) {
                    transitionId = sequenceFlow.getId();
                    break;
                }
            }

            runtimeService.createProcessInstanceModification(processInstanceId)
                    //取消现有的活动实例
                    .cancelActivityInstance(activity.getId())
                    //设置备注
                    .setAnnotation("驳回")
                    .startTransition(transitionId)
                    .execute();
        } catch (Exception e) {
            log.error("驳回处理失败：{}",e.getMessage());
            throw new CamundaProcessException("驳回处理失败");
        }
        return true;
    }


}
